package com.gitee.ixtf.vertx.internal

import cn.hutool.log.Log
import com.gitee.ixtf.guice.Jguice
import io.vertx.core.Context
import io.vertx.core.Handler
import io.vertx.core.Vertx
import io.vertx.core.impl.ContextInternal
import java.lang.UnsupportedOperationException
import java.util.concurrent.AbstractExecutorService
import java.util.concurrent.Callable
import java.util.concurrent.Delayed
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.Runnable

object DataLoaderScheduledExecutorService : AbstractExecutorService(), ScheduledExecutorService {
  private val log = Log.get("DataLoader")
  private val vertx: Vertx by Jguice
  private val vertxContext by lazy { vertx.orCreateContext as ContextInternal }

  override fun execute(command: Runnable) {
    log.warn("execute")
    val ctx =
        ContextInternal.current()?.let { current ->
          if (current.unwrap() == vertxContext) current else vertxContext
        } ?: vertxContext
    ctx.runOnContext { command.run() }
  }

  override fun schedule(command: Runnable, delay: Long, unit: TimeUnit): ScheduledFuture<*> {
    log.warn("schedule")
    val t = VertxScheduledFuture(vertxContext, command, delay, unit)
    t.schedule()
    return t
  }

  override fun scheduleAtFixedRate(
      command: kotlinx.coroutines.Runnable,
      initialDelay: Long,
      period: Long,
      unit: TimeUnit?
  ): ScheduledFuture<*> {
    throw UnsupportedOperationException("should not be called")
  }

  override fun <V : Any?> schedule(
      callable: Callable<V>?,
      delay: Long,
      unit: TimeUnit?
  ): ScheduledFuture<V> {
    throw UnsupportedOperationException("should not be called")
  }

  override fun scheduleWithFixedDelay(
      command: Runnable?,
      initialDelay: Long,
      delay: Long,
      unit: TimeUnit?
  ): ScheduledFuture<*> {
    throw UnsupportedOperationException("should not be called")
  }

  override fun isTerminated(): Boolean {
    throw UnsupportedOperationException("should not be called")
  }

  override fun shutdown() {
    throw UnsupportedOperationException("should not be called")
  }

  override fun shutdownNow(): MutableList<Runnable> {
    throw UnsupportedOperationException("should not be called")
  }

  override fun isShutdown(): Boolean {
    throw UnsupportedOperationException("should not be called")
  }

  override fun awaitTermination(timeout: Long, unit: TimeUnit?): Boolean {
    throw UnsupportedOperationException("should not be called")
  }
}

private class VertxScheduledFuture(
    val vertxContext: Context,
    val task: Runnable,
    val delay: Long,
    val unit: TimeUnit
) : ScheduledFuture<Any>, Handler<Long> {

  // pending : null (no completion)
  // done : true
  // cancelled : false
  val completion = AtomicReference<Boolean?>()
  var id: Long? = null

  fun schedule() {
    val owner = vertxContext.owner()
    id = owner.setTimer(unit.toMillis(delay), this)
  }

  override fun get(): Any? {
    return null
  }

  override fun get(timeout: Long, unit: TimeUnit?): Any? {
    return null
  }

  override fun isCancelled(): Boolean {
    return completion.get() == false
  }

  override fun handle(event: Long?) {
    if (completion.compareAndSet(null, true)) {
      task.run()
    }
  }

  override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
    return if (completion.compareAndSet(null, false)) {
      vertxContext.owner().cancelTimer(id!!)
    } else {
      false
    }
  }

  override fun isDone(): Boolean {
    return completion.get() == true
  }

  override fun getDelay(u: TimeUnit): Long {
    return u.convert(unit.toNanos(delay), TimeUnit.NANOSECONDS)
  }

  override fun compareTo(other: Delayed): Int {
    return getDelay(TimeUnit.NANOSECONDS).compareTo(other.getDelay(TimeUnit.NANOSECONDS))
  }
}
